#define NOMINMAX
#include "TreasuryFuturesList.h"
#include "../StringResource.h"
#include "../data/TreasuryFuturesData.h"
#include <core/StyleMgr.h>
#include <core/ImageMgr.h>
#include <widgets/table/STable.h>
#include <widgets/table/STableColumnSet.h>
#include <QVBoxLayout>
#include <QTableView>
#include <QHeaderView>
#include <uam/ServerTimeMgr.h>
#include <bondlib/BondContainer.h>
#include <bondlib/RealTimeDeal/BondMarketStreamV2.h>
#include <bondlib/BondMarketStream.h>
#include <tfbondlib/tfCTDInfo.h>

#define NAT_COMPARE_FLOAT 0.001
#define MIN_PRICE_CHANGE  1e-5

TreasuryFuturesList::TreasuryFuturesList(QWidget* p, CFixedNatFutureData* data)
	: QWidget(p),
	  m_table(nullptr),
	  m_model(nullptr),
	  m_tableSet(nullptr),
	  m_data(data)
{
	setMinimumHeight(109);

	m_table = new SVirtualTable(this);
	m_model = new DefaultTableModel(this);
	m_model->setTable(m_table);

	QVBoxLayout* mainLayout = new QVBoxLayout(this);
	mainLayout->setContentsMargins(0, 0, 0, 0);
	mainLayout->addWidget(m_table);
	setLayout(mainLayout);

	m_table->setHeaderSortable(true);
	m_table->table()->setModel(m_model);
	m_table->table()->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
	m_table->table()->horizontalHeader()->setSectionsMovable(true);
	m_table->table()->horizontalHeader()->show();
	m_table->table()->setContextMenuPolicy(Qt::CustomContextMenu);
	initTable();

	connect(m_table->table(), &QTableView::clicked, this, &TreasuryFuturesList::onListRowClicked);
}

void TreasuryFuturesList::onListRowClicked(const QModelIndex& index) {
	QString TFId = index.data(DataIdRole).toString();
	if (!TFId.isEmpty()) {
		emit clickedRow(TFId);
	}
}

void TreasuryFuturesList::onRefresh()
{
	updateTable(false);
}

void TreasuryFuturesList::initTable()
{
#define ALIGN_RIGHT (Qt::Alignment)(Qt::AlignVCenter|Qt::AlignRight)
#define ALIGN_LEFT  (Qt::Alignment)(Qt::AlignVCenter|Qt::AlignLeft)

	m_tableSet = new STableColumnSet(m_table, "TreasuryFuturesList", this);
	m_tableSet->setSaveCfg(true);

	BEGIN_COLUMN;
	ADD_COLUMN(fuCol_TF, m_tableSet, NewStringTable::strFixedIncomeTreasuryFuturesList(fuCol_TF), 0, 70, 70, 0.1, false, ALIGN_LEFT, kFixed, "");
	m_cols.push_back(fuCol_TF);
	ADD_COLUMN(fuCol_Prc, m_tableSet, NewStringTable::strFixedIncomeTreasuryFuturesList(fuCol_Prc), 0, 75, 12, 0.1, false, ALIGN_RIGHT, kAuto, "");
	m_cols.push_back(fuCol_Prc);
	ADD_COLUMN(fuCol_Chg, m_tableSet, NewStringTable::strFixedIncomeTreasuryFuturesList(fuCol_Chg), 0, 75, 12, 0.1, false, ALIGN_RIGHT, kAuto, "");
	m_cols.push_back(fuCol_Chg);
	ADD_COLUMN(fuCol_Rate, m_tableSet, NewStringTable::strFixedIncomeTreasuryFuturesList(fuCol_Rate), 0, 80, 12, 0.1, false, ALIGN_RIGHT, kAuto, "");
	m_cols.push_back(fuCol_Rate);
	ADD_COLUMN(fuCol_BP, m_tableSet, NewStringTable::strFixedIncomeTreasuryFuturesList(fuCol_BP), 1, 75, 12, 0.1, false, ALIGN_RIGHT, kAuto, "");
	m_cols.push_back(fuCol_BP);
	ADD_COLUMN(fuCol_Vol, m_tableSet, NewStringTable::strFixedIncomeTreasuryFuturesList(fuCol_Vol), 1, 75, 12, 0.1, false, ALIGN_RIGHT, kAuto, "");
	m_cols.push_back(fuCol_Vol);
	ADD_COLUMN(fuCol_Hold, m_tableSet, NewStringTable::strFixedIncomeTreasuryFuturesList(fuCol_Hold), 1, 75, 12, 0.1, false, ALIGN_RIGHT, kAuto, "");
	m_cols.push_back(fuCol_Hold);
	ADD_COLUMN(fuCol_CTD, m_tableSet, NewStringTable::strFixedIncomeTreasuryFuturesList(fuCol_CTD), 1, 75, 12, 0.1, false, ALIGN_RIGHT, kAuto, "");
	m_cols.push_back(fuCol_CTD);
	ADD_COLUMN(fuCol_CTD_Trading, m_tableSet, NewStringTable::strFixedIncomeTreasuryFuturesList(fuCol_CTD_Trading), 1, 75, 12, 0.1, false, ALIGN_RIGHT, kAuto, "");
	m_cols.push_back(fuCol_CTD_Trading);
	ADD_COLUMN(fuCol_Basis, m_tableSet, NewStringTable::strFixedIncomeTreasuryFuturesList(fuCol_Basis), 1, 80, 12, 0.1, false, ALIGN_RIGHT, kAuto, "");
	m_cols.push_back(fuCol_Basis);
	m_tableSet->apply();
}

void TreasuryFuturesList::updateTable(bool reset)
{
	m_table->clearTable();

	QFont f = font();
	f.setPixelSize(13);
	f.setBold(true);
	int realCount = m_data->GetSortTFSize();
	int pageCount = std::min(m_table->pageCount(), realCount);
	int pos = reset ? 0 : m_table->sliderPos();
	m_table->setRealDataCount(realCount);
	int colCount = m_model->columnCount();

	QString currentTFID = m_data->GetCurrentTFID();
	CFixedNatFutureTFData tfData(false);
	QString strText;

	for (int row = 0; row < pageCount; ++row){
		if (pos+row >= realCount){
			break;
		}

		const QString& strTF = m_data->GetSortTF(pos+row);
		bool succeed = m_data->GetData(strTF, tfData);
		if (!succeed) {
			continue;
		}

		QColor clrPrice = StyleMgr::instance().color("Highlight");
		if (tfData.m_PreClosePrice > 1 && tfData.m_LastPrice > 1) {
			double dbGap = tfData.m_LastPrice - tfData.m_PreClosePrice;
			if (dbGap > NAT_COMPARE_FLOAT) {
				clrPrice = StyleMgr::instance().color("Up_Increase_H1");
			}
			else if (dbGap < -NAT_COMPARE_FLOAT) {
				clrPrice = StyleMgr::instance().color("Down_Reduce_H1");
			}
		}

		CCTDGetInfo ctd;
		ctd.m_strTFID = tfData.m_strTFId.toStdString();

		getCTDInfo().loadCTDInfo(ctd, true);
		QString strCTD = "--", strRate = "--", strBP = "--";
		QColor clrBP = StyleMgr::instance().color("Highlight");

		std::string strCmbBondKey;
		getCTDRateBP(tfData, ctd, strCTD, strRate, strBP, clrBP, strCmbBondKey);

		MarketStreamInfo msi;
		if(!strCmbBondKey.empty())
			CMarketStream::bondStream().GetMarketStreamByKey(strCmbBondKey.c_str(), "", msi);

		for (int col=0; col<colCount; ++col){
			if (!m_tableSet->isColVisible(col)){
				continue;
			}

			QStandardItem* item = m_model->item(row, col);
			if (item == nullptr) {
				item = new QStandardItem();
			}
			item->clearData();
			item->setData(StyleMgr::instance().color("Highlight"), Qt::ForegroundRole);
			item->setData((int)(Qt::AlignLeft|Qt::AlignVCenter), Qt::TextAlignmentRole);
			item->setData(StyleMgr::instance().color("BasicPlate"), Qt::BackgroundRole);
			item->setData(f, Qt::FontRole);
			item->setData(tfData.m_strTFId, DataIdRole);

			switch (col) {
			case fuCol_TF: {//合约			
				strText = tfData.m_strTFId;
				item->setData(strText, Qt::DisplayRole);
				break;
			}
			case fuCol_Prc: { //最新价
				if (tfData.m_LastPrice > NAT_COMPARE_FLOAT) {
					strText = QString::asprintf("%.3f", tfData.m_LastPrice);
				}
				else {
					strText = "--";
				}
				item->setData(clrPrice, Qt::ForegroundRole);
				item->setData((int)(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
				item->setData(strText, Qt::DisplayRole);
				break;
			}
			case fuCol_Chg: {//涨跌
				if (tfData.m_LastPrice>NAT_COMPARE_FLOAT && tfData.m_PreClosePrice>NAT_COMPARE_FLOAT) {
					strText = QString::asprintf("%.3f", tfData.m_LastPrice-tfData.m_PreClosePrice);
				}
				else {
					strText = "--";
				}
				item->setData(clrPrice, Qt::ForegroundRole);
				item->setData((int)(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
				item->setData(strText, Qt::DisplayRole);
				break;
			}
			case fuCol_Rate: {//隐含利率
				strText = strRate;
				item->setData((int)(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
				item->setData(strText, Qt::DisplayRole);
				break;
			}
			case fuCol_BP: {//BP
				enum { kPriceNone = 0, kPriceRise, kPriceFall };
				if (strBP == "--"){
					strText = strBP;
				}
				else {
					double fprice = strBP.toDouble();
					if (fprice > MIN_PRICE_CHANGE) {
						strText = strBP;
					}
					else if (fprice < -MIN_PRICE_CHANGE) {
						strText = strBP;
					}
					else {
						strText = QString::asprintf("%.3f", 0.0);
					}
				}

				item->setData(clrBP, Qt::ForegroundRole);
				item->setData((int)(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
				item->setData(strText, Qt::DisplayRole);
				break;
			}
			case fuCol_Vol: {//成交量(手)
				if (tfData.m_fVolumn > NAT_COMPARE_FLOAT) {
					strText = QString::asprintf("%.0f", tfData.m_fVolumn);
				}
				else {
					strText = "--";
				}
				item->setData((int)(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
				item->setData(strText, Qt::DisplayRole);
				break;
			}
			case fuCol_Hold: {//持仓量(手)
				if (tfData.m_Hold > NAT_COMPARE_FLOAT) {
					strText = QString::asprintf("%.0f", tfData.m_Hold);
				}
				else {
					strText = "--";
				}
				item->setData((int)(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
				item->setData(strText, Qt::DisplayRole);
				break;
			}
			case fuCol_CTD: {//CTD
				strText = strCTD;
				item->setData((int)(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
				item->setData(strText, Qt::DisplayRole);
				break;
			}
			case fuCol_CTD_Trading: {//CTD成交(%)
				strText = "--";
				if (!strCmbBondKey.empty()) {
					double yield = getBondDealYield(msi);
					if (yield > NAT_COMPARE_FLOAT) {
						strText = QString::asprintf("%.4f", yield);
					}
				}
				item->setData((int)(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
				item->setData(strText, Qt::DisplayRole);
				break;
			}
			case fuCol_Basis: {//基差(元)
				strText = "--";
				if (!strCmbBondKey.empty()) {
					double price = getBondDealPrice(msi);
					if (tfData.m_LastPrice>NAT_COMPARE_FLOAT &&
						price>NAT_COMPARE_FLOAT) {
						strText = QString::asprintf("%.4f", price-tfData.m_LastPrice*ctd.m_fFactor);
					}
				}
				item->setData((int)(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
				item->setData(strText, Qt::DisplayRole);
				break;
			}
			default: break;
			}
			m_model->setItem(row, col, item);
		}
	}

	if (realCount > 0) {
		QModelIndex index = m_table->table()->selectionModel()->currentIndex();
		if (index.row() <= 0) {
			m_table->table()->selectRow(0);
			index = m_table->table()->selectionModel()->currentIndex();
			onListRowClicked(index);
		}
	}
	//updateFlash();
}

void TreasuryFuturesList::getCTDRateBP(const CFixedNatFutureTFData& tfPrice, const CCTDGetInfo& ctd, QString& strCTD, QString& strRate, QString& strBP, QColor& crFgClr, std::string& bondkey) {
	bondkey = "";
	if (tfPrice.m_LastPrice<0.1 || tfPrice.m_PreClosePrice<0.1) {
		return;
	}

	strCTD = strRate = strBP = "--";
	if (!ctd.m_strBondKey.empty() && (ctd.m_dbBasicPrc<-999.01 || ctd.m_dbBasicPrc>-998.00)) {
		int bondIndex = CBondContainer::instance().GetBondIndex(ctd.m_strBondKey.c_str(), "CIB");
		if (CBondContainer::instance().IsValidIndex(bondIndex)) {
			CBondInfo* bondInfo = CBondContainer::instance().GetBond(bondIndex);
			strCTD = QString::asprintf("%s", bondInfo->GetCombBondCode());
			bondkey = bondInfo->GetCombBondKey();

			if (verifyValidCalculate(ctd.m_dbTFYeild, true)) {
				strRate = QString::asprintf("%.4f", ctd.m_dbTFYeild);
			}

			if (verifyValidCalculate(ctd.m_dbBP, true)) {
				if (ctd.m_dbBP > 0.001) {
					crFgClr = StyleMgr::instance().color("Down_Reduce_H1");
					strBP = QString::asprintf("+%.3f", ctd.m_dbBP);
				} else if (ctd.m_dbBP < -0.001) {
					crFgClr = StyleMgr::instance().color("Up_Increase_H1");
					strBP = QString::asprintf("%.3f", ctd.m_dbBP);
				} else {
					strBP = "0";
				}
			}
		}
	}
}

bool TreasuryFuturesList::verifyValidCalculate(double data, bool bNeedZero)
{
	if ((data > -99899.00 || data < -99901.00) &&
		(data > -998.99 || data < -999.01) &&
		(bNeedZero || (!bNeedZero && (data > 0.0000001 || data < -0.0000001))))
		return true;
	return false;
}

double TreasuryFuturesList::getBondDealYield(const MarketStreamInfo& mtk) const {
	if (strcmp(mtk.m_type, "2") == 0) {
		int state = atoi(mtk.m_body.m_dealStatus);
		if (state < 4) {
			double price = atof(mtk.m_body.m_price);
			if (price > 30) {
				price = atof(mtk.m_body.m_yield);
			}
			return price;
		}
	}
	return 0.0f;
}

double TreasuryFuturesList::getBondDealPrice(const MarketStreamInfo& mtk) const{
	if (strcmp(mtk.m_type, "2")==0 &&
		atoi(mtk.m_body.m_dealStatus)<4) {
		return atof(mtk.m_body.m_cleanPrice);
	}
	return 0.0f;
}